library(pdftools)
library(tidyverse)
library(magrittr)
library(stringi)
library(tidytext)
library(igraph)
library(ggraph)
library(ggplot2)
Loading data
ocpt %>% glimpse()
Rows: 7,424
Columns: 6
Groups: orig_id, organization, fullpath, gcode [697]
$ orig_id <int> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
$ organization <chr> "1&1 Drillisch AG", "1&1 Drillisch AG", "1&1 Drillisch AG", "1&1 Drillisch AG", "1&1 Drillisch AG", "1&1 Drillisch AG", "1&1 Drillisch AG", "1&1 Drillis…
$ fullpath <chr> "reports/11_drillisch_ag/2020/sustainability_report_2020.pdf", "reports/11_drillisch_ag/2020/sustainability_report_2020.pdf", "reports/11_drillisch_ag/2…
$ gcode <dbl> 401, 401, 403, 403, 404, 405, 405, 405, 405, 406, 406, 408, 409, 414, 414, 414, 416, 417, 417, 417, 418, 418, 999, 999, 999, 999, 999, 999, 999, 999, 99…
$ pdf_page <dbl> 38, 46, 48, 50, 41, 44, 9, 46, 48, 44, 45, 68, 68, 67, 68, 66, 25, 33, 55, 35, 28, 30, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2…
$ text <chr> "1&1 DRILLISCH AG SUSTAINABILITY REPORT 2019\n38\nRESULTS AND ASSESSMENT\nWe use a range of performance indicators to measure the effectiveness of our h…
Prepare sets
#for modelling
ocpt2 <- ocpt %>% ungroup()%>% distinct(fullpath, pdf_page, .keep_all = TRUE)
#for text mining
ocpt3 <- ocpt %>% filter(!gcode=="999") %>% ungroup() %>% distinct(fullpath, pdf_page, .keep_all = TRUE)
ocpt3 %<>% mutate(doc_id=paste0(orig_id, ", ", gcode))
Annotating text
library(udpipe)
udmodel <- udpipe_load_model(file = "english-ewt-ud-2.5-191206.udpipe")
up <- udpipe_annotate(udmodel, x = ocpt3$text, doc_id = ocpt3$doc_id)
up <- as.data.frame(up)
Making a list of verbs from available texts
stats <- subset(up, upos %in% c("VERB"))
stats <- txt_freq(stats$lemma)
stats %>% head(50)
write.csv(stats, "verbs.csv")
Uploading a list of grouped verbs
To find synonims for English words we can use the WordNet and its R wrapper in the wordnet package. (https://bernhardlearns.blogspot.com/2017/04/cleaning-words-with-r-stemming.html)
verbs %<>% str_trim(verb)
Error in match.arg(side) : object 'verb' not found
#verbs_qp <- verbs %>% filter(character=="QN") %>% select(verb)
up2 <- up %>% subset(upos %in% c("VERB"))
up2 %<>% subset(lemma %in% as.vector(verbs$verb))
up2 %<>% merge(txt_freq(up2$lemma), by.x= "lemma", by.y = "key")
# up2 %<>% count(lemma) %>% mutate(freq = n / sum(n)) %>% arrange(desc(n))
up2 %<>% left_join(verbs, by=c("lemma"="verb"))
up2 %<>% unique()
up2 %<>% separate(doc_id, c("doc", "gcode"), sep = ", ")
# verbs %<>% merge(up2, by.x = "verb", by.y="lemma")
up2 %>%
distinct(lemma, .keep_all = T) %>%
group_by(character) %>% arrange(desc(freq)) %>% slice(1:12) %>% ungroup() %>%
mutate(verb = reorder_within(lemma, by = freq, within = character)) %>%
ggplot(aes(x = verb, y = freq, fill = character)) +
geom_col(show.legend = FALSE) +
labs(x = "Verbs", y = "frequency") +
facet_wrap(~character, ncol = 3, scales = "free_y") +
coord_flip() +
scale_x_reordered() +
theme(axis.text.y = element_text(size = 6))

up2 %>%
distinct(lemma, .keep_all = T) %>%
group_by(character) %>% summarize(sum=sum(freq)) %>% #arrange(desc(sum)) %>%
ggplot(aes(x = character, y = sum)) +
geom_bar(stat = "sum",show.legend = FALSE)

up2 %>%
group_by(gcode,character) %>% count(lemma) %>% summarize(sum=sum(n), .groups = "keep") %>%
mutate(character = reorder_within(character, by = sum, within = gcode)) %>%
ggplot(aes(x = character, y = sum, fill = gcode)) +
geom_col(show.legend = FALSE) +
labs(x = "Verbs", y = "frequency") +
facet_wrap(~gcode, ncol = 4, scales = "free_y") +
coord_flip() +
scale_x_reordered() +
theme(axis.text.y = element_text(size = 6))

verb_phrase_simp <- "((A|N)*N(P+D*(A|N)*N)*P*(M|V)*V(M|V)*|(M|V)*V(M|V)*D*(A|N)*N(P+D*(A|N)*N)*|(M|V)*V(M|V)*(P+D*(A|N)*N)+|(A|N)*N(P+D*(A|N)*N)*P*((M|V)*V(M|V)*D*(A|N)*N(P+D*(A|N)*N)*|(M|V)*V(M|V)*(P+D*(A|N)*N)+))" # Simple verb Phrase
verb_phrase_with_cc <- "(((A(CA)*|N)*N((P(CP)*)+(D(CD)*)*(A(CA)*|N)*N)*(C(D(CD)*)*(A(CA)*|N)*N((P(CP)*)+(D(CD)*)*(A(CA)*|N)*N)*)*)(P(CP)*)*(M(CM)*|V)*V(M(CM)*|V)*(C(M(CM)*|V)*V(M(CM)*|V)*)*|(M(CM)*|V)*V(M(CM)*|V)*(C(M(CM)*|V)*V(M(CM)*|V)*)*(D(CD)*)*((A(CA)*|N)*N((P(CP)*)+(D(CD)*)*(A(CA)*|N)*N)*(C(D(CD)*)*(A(CA)*|N)*N((P(CP)*)+(D(CD)*)*(A(CA)*|N)*N)*)*)|(M(CM)*|V)*V(M(CM)*|V)*(C(M(CM)*|V)*V(M(CM)*|V)*)*((P(CP)*)+(D(CD)*)*(A(CA)*|N)*N)+|((A(CA)*|N)*N((P(CP)*)+(D(CD)*)*(A(CA)*|N)*N)*(C(D(CD)*)*(A(CA)*|N)*N((P(CP)*)+(D(CD)*)*(A(CA)*|N)*N)*)*)(P(CP)*)*((M(CM)*|V)*V(M(CM)*|V)*(C(M(CM)*|V)*V(M(CM)*|V)*)*(D(CD)*)*((A(CA)*|N)*N((P(CP)*)+(D(CD)*)*(A(CA)*|N)*N)*(C(D(CD)*)*(A(CA)*|N)*N((P(CP)*)+(D(CD)*)*(A(CA)*|N)*N)*)*)|(M(CM)*|V)*V(M(CM)*|V)*(C(M(CM)*|V)*V(M(CM)*|V)*)*((P(CP)*)+(D(CD)*)*(A(CA)*|N)*N)+))" # Verb phrase with coordination conjunction
up3 <- up %>% subset(sentence %in% up2$sentence)
up3 %<>% separate(doc_id, c("doc", "gcode"), sep = ", ")
up3 %<>% mutate(phrase_tag=as_phrasemachine(upos,type="upos"))
verb_phrases <- keywords_phrases(up3$phrase_tag, term = up3$token,
pattern = verb_phrase_simp, is_regex = TRUE,
ngram_max = 5,
detailed = TRUE)
head(sort(table(verb_phrases$keyword), decreasing=TRUE), 20)
we have that are WE GET WE GET THINGS
146 138 136 136
WE GET THINGS DONE employees are it is which is
130 126 124 117
We are organization shall organization shall report organization shall report its
108 102 102 102
organization shall report its management report its report its management report its management approach
102 102 102 102
reporting organization shall reporting organization shall report reporting organization shall report its shall report its
102 102 102 102
verb_phrases <- lemmed %>%
group_by(start, end) %>%
summarise(string = c(word)) %>%
rowwise()
nest_by(ngram, pattern, start, end)
Trying to find cooccurrences amoung slected verbs and nouns — some uncertainty introduced since lemma can be both noun and a verb.
cooc <- cooccurrence(x = subset(up3, upos %in% c("NOUN", "VERB")),
term = "lemma",
group = c("doc", "gcode", "paragraph_id", "sentence_id"))
cooc %<>% subset(term1 %in% verbs$verb | term2 %in% verbs$verb)
cooc %>% str()
Classes ‘cooccurrence’, ‘data.table’ and 'data.frame': 65771 obs. of 3 variables:
$ term1: chr "impact" "page" "employee" "employee" ...
$ term2: chr "rights" "risk" "risk" "provide" ...
$ cooc : num 1063 955 915 871 741 ...

Top 5 verbs in every verb group
Most cooccured nouns with verbs by character in 401 code

Top Nouns cooccured with verbs by every verb-character

library(igraph)
library(ggraph)
library(ggplot2)
plot_annotation <- function(x, size = 3){
stopifnot(is.data.frame(x) & all(c("sentence_id", "token_id", "head_token_id", "dep_rel",
"token", "lemma", "upos", "xpos", "feats") %in% colnames(x)))
x <- x[!is.na(x$head_token_id), ]
x <- x[x$sentence_id %in% min(x$sentence_id), ]
edges <- x[x$head_token_id != 0, c("token_id", "head_token_id", "dep_rel")]
edges$label <- edges$dep_rel
g <- graph_from_data_frame(edges,
vertices = x[, c("token_id", "token", "lemma", "upos", "xpos", "feats")],
directed = TRUE)
ggraph(g, layout = "linear") +
geom_edge_arc(ggplot2::aes(label = dep_rel, vjust = -0.20),
arrow = grid::arrow(length = unit(4, 'mm'), ends = "last", type = "closed"),
end_cap = ggraph::label_rect("wordswordswords"),
label_colour = "red", check_overlap = TRUE, label_size = size) +
geom_node_label(ggplot2::aes(label = token), col = "darkgreen", size = size, fontface = "bold") +
geom_node_text(ggplot2::aes(label = upos), nudge_y = -0.35, size = size) +
theme_graph(base_family = "Arial Narrow") +
labs(title = "udpipe output", subtitle = "tokenisation, parts of speech tagging & dependency relations")
}
plot_annotation(up3[645:663,])


up3[645:663,]
NA
undata2 <- pre_pro_rep %>%
unnest_tokens(bigram,text,token = "ngrams", n=2) #%>%
#mutate(bigram = bigram %>% str_remove_all("[^[:alnum:]]")) %>%
undata1 <- pre_pro_rep %>%
unnest_tokens(word,text,token = "ngrams", n=1) %>%
mutate(word = word %>% str_remove_all("[^[:alnum:]]")) %>%
mutate(word = word %>% str_remove_all(rem_dig)) %>%
mutate(word = word %>% str_remove_all("null")) %>%
filter(!is.na(word)) %>%
anti_join(stop_words, by = "word")
bigrams_separated <- undata2 %>% separate(bigram, c("word1", "word2"), sep = " ")
bigrams_filtered <- bigrams_separated %>% filter(!word1 %in% stop_words$word) %>% filter(!word2 %in% stop_words$word)
# new bigram counts:
bigram_counts <- bigrams_filtered %>% count(word1, word2, sort = TRUE)
bigrams_united <- bigrams_filtered %>% unite(bigram, word1, word2, sep = " ")
bigrams_united %<>% add_count(X,bigram) %>% bind_tf_idf(term = bigram, document = X, n = n)
words <- undata1 %>% add_count(X,word) %>% bind_tf_idf(term = word, document = X, n = n)
bigrams_united %>% count(bigram, wt = tf_idf, sort = TRUE) %>% head(25)
top_by_g <- bigrams_united %>% group_by(gcode) %>% count(bigram, wt = tf_idf, sort = TRUE, name = "tf_idf") %>% dplyr::slice(1:12) %>% ungroup()
top_by_g <- words %>% group_by(gcode) %>% count(word, wt = tf_idf, sort = TRUE, name = "tf_idf") %>% dplyr::slice(1:5) %>% ungroup()
top_by_y <- bigrams_united %>% group_by(rep_year) %>% count(bigram, wt = tf_idf, sort = TRUE, name = "tf_idf") %>% dplyr::slice(1:12) %>% ungroup()
# top bigrams in each g_code
top_by_g %>% mutate(bigram = reorder_within(bigram, by = tf_idf, within = gcode)) %>%
ggplot(aes(x = bigram, y = tf_idf, fill = gcode)) +
geom_col(show.legend = FALSE) +
labs(x = NULL, y = "tf-idf") +
facet_wrap(~gcode, ncol = 3, scales = "free") +
coord_flip() +
scale_x_reordered() +
theme(axis.text.y = element_text(size = 6))
top_by_g %>% mutate(word = reorder_within(word, by = tf_idf, within = gcode)) %>%
ggplot(aes(x = word, y = tf_idf, fill = gcode)) +
geom_col(show.legend = FALSE) +
labs(x = NULL, y = "tf-idf") +
facet_wrap(~gcode, ncol = 3, scales = "free") +
coord_flip() +
scale_x_reordered() +
theme(axis.text.y = element_text(size = 8))
library(textdata)
library(recipes)
library(tidymodels)
library(textrecipes)
library(themis)
library(tune)
library(glmnet)
library(ranger)
mdata <- ocpt2 %>% ungroup() %>% select(gcode,text)
#glove6b <- embedding_glove6b(dimensions = 100)
set.seed(1234)
rem_punct <- regex("[[:punct:]]")
rem_dig <- regex("[[:digit:]]")
mdata %<>% mutate(text=str_squish(text),
text=str_remove_all(text,rem_punct),
text=str_remove_all(text,rem_dig))
mdata <-mdata[-c(no_label_index[c(random_indexes)])]
Error: Can't negate columns that don't exist.
x Locations 3265, 6190, 3919, 195, 4845, etc. don't exist.
ℹ There are only 2 columns.
Run `rlang::last_error()` to see where the error occurred.
write.csv(mdata, "mdata_2.csv")
tidy_split <- initial_split(mdata, strata = gcode, prop = 0.7)
train_data <- training(tidy_split)
test_data <- testing(tidy_split)
tidy_split
<Analysis/Assess/Total>
<662/287/949>
train_data <- recipe(gcode~., data = train_data) %>% themis::step_upsample(gcode) %>% prep() %>% juice()
train_data %<>% mutate(text=as.character(text))
data_res <- train_data %>% vfold_cv(strata = gcode, v = 10, repeats = 3)
data_res <- vfold_cv(train_data)
tf_idf_rec <- recipe(gcode ~ ., data = train_data) %>%
step_tokenize(text) %>%
step_stem(text) %>%
step_stopwords(text) %>%
step_tokenfilter(text, max_tokens = 2000) %>%
step_tfidf(all_predictors())
tf_idf_data <- tf_idf_rec %>% prep() %>% juice()
hash_rec <- recipe(gcode~., data = train_data) %>%
step_tokenize(text) %>%
step_stem(text) %>%
step_stopwords(text) %>%
step_tokenfilter(text, max_tokens = 1000) %>%
step_texthash(text, num_terms = 100)
hash_rec %>% prep() %>% juice()
NA
model_lg <- multinom_reg() %>%
set_args(penalty=tune(), mixture=NULL) %>%
set_engine("glmnet") %>%
set_mode("classification")
model_rf <- rand_forest() %>%
set_engine("ranger", importance = "impurity") %>%
set_mode("classification")
logistic_grid <- grid_regular(parameters(model_lg), levels = 3)
model_control <- control_grid(save_pred = TRUE)
model_metrics <- metric_set(accuracy, roc_auc)
linear_tf_res <- tune_grid(model_lg, tf_idf_rec, grid = logistic_grid, control = model_control, metrics = model_metrics, resamples = data_res)
linear_hash_res <- tune_grid(model_lg, hash_rec, grid = logistic_grid, control = model_control, metrics = model_metrics, resamples = data_res)
workflow_general_tf <- workflow() %>% add_recipe(tf_idf_rec)
workflow_lg_tf <- workflow_general_tf %>% add_model(model_lg)
workflow_rf_tf <- workflow_general_tf %>% add_model(model_rf)
workflow_general_hash <- workflow() %>% add_recipe(hash_rec)
workflow_lg_hash <- workflow_general_hash %>% add_model(model_lg)
workflow_rf_hash <- workflow_general_hash %>% add_model(model_rf)
linear_tf_res %>% autoplot()

best_param_linear_tf_res <- linear_tf_res %>% select_best(metric = 'accuracy')
best_param_linear_tf_res
workflow_final_lg_tf <- workflow_lg_tf %>%
finalize_workflow(parameters = best_param_linear_tf_res)
log_res_tf <- workflow_final_lg_tf %>%
fit_resamples(resamples = data_res,
metrics = metric_set(recall, precision, f_meas, accuracy, kap, roc_auc, sens), control = control_resamples(save_pred = TRUE))
log_res_tf %>% collect_metrics(summarize = TRUE)
linear_hash_res %>% autoplot()

best_param_linear_hash_res <- linear_hash_res %>% select_best(metric = 'accuracy')
best_param_linear_hash_res
workflow_final_lg_hash <- workflow_lg_hash %>%
finalize_workflow(parameters = best_param_linear_hash_res)
log_res_hash <- workflow_final_lg_hash %>%
fit_resamples( resamples = data_res,
metrics = metric_set(recall, precision, f_meas, accuracy, kap, roc_auc, sens), control = control_resamples(save_pred = TRUE))
log_res_hash %>% collect_metrics(summarize = TRUE)
rf_res_hash <- workflow_rf_hash %>%
fit_resamples(resamples = data_res,
metrics = metric_set(recall, precision, f_meas, accuracy, kap, roc_auc, sens),
control = control_resamples( save_pred = TRUE))
rf_res_hash %>% collect_metrics(summarize = TRUE)
NA
rf_res_tf <- workflow_rf_tf %>%
fit_resamples(resamples = data_res,
metrics = metric_set(recall, precision, f_meas, accuracy, kap, roc_auc, sens),
control = control_resamples( save_pred = TRUE))
rf_res_tf %>% collect_metrics(summarize = TRUE)
NA
log_metrics_tf <- log_res_tf %>% collect_metrics(summarise = TRUE) %>% mutate(model = "Logistic Regression TF-idf")
log_metrics_hash <- log_res_hash %>% collect_metrics(summarise = TRUE) %>% mutate(model = "Logistic Regression Hash")
rf_metrics_tf <- rf_res_tf %>% collect_metrics(summarise = TRUE) %>% mutate(model = "Random Forest TF-idf")
rf_metrics_hash <- rf_res_hash %>% collect_metrics(summarise = TRUE) %>% mutate(model = "Random Forest Hash")
model_compare <- bind_rows(
log_metrics_tf,
log_metrics_hash,
rf_metrics_tf,
rf_metrics_hash)
rf_stat1 <- rf_res_tf %>% collect_metrics(summarize = TRUE)
rf_stat1
model_comp <- model_compare %>%
select(model, .metric, mean, std_err) %>%
pivot_wider(names_from = .metric, values_from = c(mean, std_err))
model_comp %>%
arrange(mean_f_meas) %>%
mutate(model = fct_reorder(model, mean_f_meas)) %>%
ggplot(aes(model, mean_f_meas, fill=model)) +
geom_col() + coord_flip() +
scale_fill_brewer(palette = "YlGn") +
geom_text(size = 3, aes(label = round(mean_f_meas, 2), y = mean_f_meas + 0.08), vjust = 1)

rf_pred_tf <- rf_res_tf %>%
collect_predictions()
rf_pred_tf %>% conf_mat(gcode, .pred_class)
Truth
Prediction 401 403 404 405 407 413 414 416 417 999
401 125 5 4 9 0 0 0 0 0 3
403 5 132 4 0 0 2 0 0 0 6
404 5 0 129 0 0 0 0 0 0 3
405 3 0 0 127 0 0 0 0 0 4
407 0 0 0 0 140 0 0 0 0 0
413 1 0 1 0 0 130 1 0 0 9
414 0 0 1 0 0 0 139 0 0 6
416 0 0 0 0 0 0 0 140 0 0
417 0 0 0 0 0 0 0 0 140 2
999 1 3 1 4 0 8 0 0 0 107
rf_pred_tf %>% conf_mat(gcode, .pred_class) %>% autoplot(type = "heatmap")


last_fit_rf <- last_fit(workflow_rf_tf,
split = tidy_split,
metrics = metric_set(recall, precision, f_meas, accuracy, kap, roc_auc, sens))
rf_stat2 <- last_fit_rf %>% collect_metrics()
rf_stat2
last_fit_rf %>% collect_predictions() %>% conf_mat(gcode, .pred_class) %>% autoplot(type = "heatmap")

myt <- rf_stat1 %>% left_join(rf_stat2, by=".metric") %>% select(.metric, mean, std_err, .estimate)
myt
NA
#stargazer(myt, type = "latex", out = "models.tex")
stargazer(myt, type = "text")
===================================================
Statistic N Mean St. Dev. Min Pctl(25) Pctl(75) Max
===================================================
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCmBgYHtyfQpsaWJyYXJ5KHBkZnRvb2xzKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShtYWdyaXR0cikKbGlicmFyeShzdHJpbmdpKQpsaWJyYXJ5KHRpZHl0ZXh0KQpsaWJyYXJ5KGlncmFwaCkKbGlicmFyeShnZ3JhcGgpCmxpYnJhcnkoZ2dwbG90MikKYGBgCgojIyBMb2FkaW5nIGRhdGEKYGBge3J9Cm9jcHQgPC0gcmVhZFJEUygib3JsaXN0X2NvZ2VzX3BhZ2VzX3RleHRfdW5uZXN0ZWQucmRzIikKb2NwdCAlPiUgZ2xpbXBzZSgpCmBgYAojIyBQcmVwYXJlIHNldHMKYGBge3J9CgojZm9yIG1vZGVsbGluZwpvY3B0MiA8LSBvY3B0ICU+JSB1bmdyb3VwKCklPiUgZGlzdGluY3QoZnVsbHBhdGgsIHBkZl9wYWdlLCAua2VlcF9hbGwgPSBUUlVFKQoKI2ZvciB0ZXh0IG1pbmluZwpvY3B0MyA8LSBvY3B0ICU+JSBmaWx0ZXIoIWdjb2RlPT0iOTk5IikgJT4lIHVuZ3JvdXAoKSAlPiUgZGlzdGluY3QoZnVsbHBhdGgsIHBkZl9wYWdlLCAua2VlcF9hbGwgPSBUUlVFKQpvY3B0MyAlPD4lIG11dGF0ZShkb2NfaWQ9cGFzdGUwKG9yaWdfaWQsICIsICIsIGdjb2RlKSkKYGBgCgojIyBBbm5vdGF0aW5nIHRleHQKYGBge3J9CmxpYnJhcnkodWRwaXBlKQp1ZG1vZGVsIDwtIHVkcGlwZV9sb2FkX21vZGVsKGZpbGUgPSAiZW5nbGlzaC1ld3QtdWQtMi41LTE5MTIwNi51ZHBpcGUiKQoKdXAgPC0gdWRwaXBlX2Fubm90YXRlKHVkbW9kZWwsIHggPSBvY3B0MyR0ZXh0LCBkb2NfaWQgPSBvY3B0MyRkb2NfaWQpCnVwIDwtIGFzLmRhdGEuZnJhbWUodXApCmBgYAoKCiMjIE1ha2luZyBhIGxpc3Qgb2YgdmVyYnMgZnJvbSBhdmFpbGFibGUgdGV4dHMKYGBge3J9CgpzdGF0cyA8LSBzdWJzZXQodXAsIHVwb3MgJWluJSBjKCJWRVJCIikpIApzdGF0cyA8LSB0eHRfZnJlcShzdGF0cyRsZW1tYSkKc3RhdHMgJT4lIGhlYWQoNTApCndyaXRlLmNzdihzdGF0cywgInZlcmJzLmNzdiIpCgpgYGAKIyMgVXBsb2FkaW5nIGEgbGlzdCBvZiBncm91cGVkIHZlcmJzCgpUbyBmaW5kIHN5bm9uaW1zIGZvciBFbmdsaXNoIHdvcmRzIHdlIGNhbiB1c2UgdGhlIFdvcmROZXQgYW5kIGl0cyBSIHdyYXBwZXIgaW4gdGhlIHdvcmRuZXQgcGFja2FnZS4gKGh0dHBzOi8vYmVybmhhcmRsZWFybnMuYmxvZ3Nwb3QuY29tLzIwMTcvMDQvY2xlYW5pbmctd29yZHMtd2l0aC1yLXN0ZW1taW5nLmh0bWwpCmBgYHtyfQp2ZXJiczwtcmVhZC5jc3YoInZlcmJzX2dyb3VwZWQuY3N2IikKCnZlcmJzICU8PiUgbXV0YXRlKHZlcmI9c3RyX3RyaW0odmVyYikpCnZlcmJzICU8PiUgdW5pcXVlKCkKYGBgCgpgYGB7cn0KI3ZlcmJzX3FwIDwtIHZlcmJzICU+JSBmaWx0ZXIoY2hhcmFjdGVyPT0iUU4iKSAlPiUgc2VsZWN0KHZlcmIpCgp1cDIgPC0gdXAgJT4lIHN1YnNldCh1cG9zICVpbiUgYygiVkVSQiIpKQp1cDIgJTw+JSBzdWJzZXQobGVtbWEgJWluJSBhcy52ZWN0b3IodmVyYnMkdmVyYikpCnVwMiAlPD4lIG1lcmdlKHR4dF9mcmVxKHVwMiRsZW1tYSksIGJ5Lng9ICJsZW1tYSIsIGJ5LnkgPSAia2V5IikKIyB1cDIgJTw+JSBjb3VudChsZW1tYSkgJT4lICBtdXRhdGUoZnJlcSA9IG4gLyBzdW0obikpICU+JSAgIGFycmFuZ2UoZGVzYyhuKSkKCnVwMiAgJTw+JSAgbGVmdF9qb2luKHZlcmJzLCBieT1jKCJsZW1tYSI9InZlcmIiKSkKdXAyICU8PiUgdW5pcXVlKCkKdXAyICU8PiUgc2VwYXJhdGUoZG9jX2lkLCBjKCJkb2MiLCAiZ2NvZGUiKSwgc2VwID0gIiwgIikKIyB2ZXJicyAlPD4lIG1lcmdlKHVwMiwgYnkueCA9ICJ2ZXJiIiwgYnkueT0ibGVtbWEiKQoKYGBgCgpgYGB7cn0KdXAyICU+JQogIGRpc3RpbmN0KGxlbW1hLCAua2VlcF9hbGwgPSBUKSAlPiUgCiAgZ3JvdXBfYnkoY2hhcmFjdGVyKSAlPiUgYXJyYW5nZShkZXNjKGZyZXEpKSAlPiUgc2xpY2UoMToxMikgJT4lIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKHZlcmIgPSByZW9yZGVyX3dpdGhpbihsZW1tYSwgYnkgPSBmcmVxLCB3aXRoaW4gPSBjaGFyYWN0ZXIpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB2ZXJiLCB5ID0gZnJlcSwgZmlsbCA9IGNoYXJhY3RlcikpICsKICAgIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsKICAgIGxhYnMoeCA9ICJWZXJicyIsIHkgPSAiZnJlcXVlbmN5IikgKwogICAgZmFjZXRfd3JhcCh+Y2hhcmFjdGVyLCBuY29sID0gMywgc2NhbGVzID0gImZyZWVfeSIpICsKICAgIGNvb3JkX2ZsaXAoKSArCiAgICBzY2FsZV94X3Jlb3JkZXJlZCgpICsKICAgIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA2KSkKYGBgCgoKYGBge3J9Cgp1cDIgJT4lIAogIGRpc3RpbmN0KGxlbW1hLCAua2VlcF9hbGwgPSBUKSAlPiUgCiAgZ3JvdXBfYnkoY2hhcmFjdGVyKSAlPiUgc3VtbWFyaXplKHN1bT1zdW0oZnJlcSkpICU+JSAjYXJyYW5nZShkZXNjKHN1bSkpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBjaGFyYWN0ZXIsIHkgPSBzdW0pKSArCiAgICBnZW9tX2JhcihzdGF0ID0gInN1bSIsc2hvdy5sZWdlbmQgPSBGQUxTRSkKCmBgYAoKCmBgYHtyfQoKdXAyICU+JSAKICBncm91cF9ieShnY29kZSxjaGFyYWN0ZXIpICU+JSBjb3VudChsZW1tYSkgJT4lIHN1bW1hcml6ZShzdW09c3VtKG4pLCAuZ3JvdXBzID0gImtlZXAiKSAlPiUKICBtdXRhdGUoY2hhcmFjdGVyID0gcmVvcmRlcl93aXRoaW4oY2hhcmFjdGVyLCBieSA9IHN1bSwgd2l0aGluID0gZ2NvZGUpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBjaGFyYWN0ZXIsIHkgPSBzdW0sIGZpbGwgPSBnY29kZSkpICsKICAgIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsKICAgIGxhYnMoeCA9ICJWZXJicyIsIHkgPSAiZnJlcXVlbmN5IikgKwogICAgZmFjZXRfd3JhcCh+Z2NvZGUsIG5jb2wgPSA0LCBzY2FsZXMgPSAiZnJlZV95IikgKwogICAgY29vcmRfZmxpcCgpICsKICAgIHNjYWxlX3hfcmVvcmRlcmVkKCkgKwogICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpKQpgYGAKYGBge3J9CnZlcmJfcGhyYXNlX3NpbXAgPC0gIigoQXxOKSpOKFArRCooQXxOKSpOKSpQKihNfFYpKlYoTXxWKSp8KE18VikqVihNfFYpKkQqKEF8TikqTihQK0QqKEF8TikqTikqfChNfFYpKlYoTXxWKSooUCtEKihBfE4pKk4pK3woQXxOKSpOKFArRCooQXxOKSpOKSpQKigoTXxWKSpWKE18VikqRCooQXxOKSpOKFArRCooQXxOKSpOKSp8KE18VikqVihNfFYpKihQK0QqKEF8TikqTikrKSkiICMgU2ltcGxlIHZlcmIgUGhyYXNlCnZlcmJfcGhyYXNlX3dpdGhfY2MgPC0gIigoKEEoQ0EpKnxOKSpOKChQKENQKSopKyhEKENEKSopKihBKENBKSp8TikqTikqKEMoRChDRCkqKSooQShDQSkqfE4pKk4oKFAoQ1ApKikrKEQoQ0QpKikqKEEoQ0EpKnxOKSpOKSopKikoUChDUCkqKSooTShDTSkqfFYpKlYoTShDTSkqfFYpKihDKE0oQ00pKnxWKSpWKE0oQ00pKnxWKSopKnwoTShDTSkqfFYpKlYoTShDTSkqfFYpKihDKE0oQ00pKnxWKSpWKE0oQ00pKnxWKSopKihEKENEKSopKigoQShDQSkqfE4pKk4oKFAoQ1ApKikrKEQoQ0QpKikqKEEoQ0EpKnxOKSpOKSooQyhEKENEKSopKihBKENBKSp8TikqTigoUChDUCkqKSsoRChDRCkqKSooQShDQSkqfE4pKk4pKikqKXwoTShDTSkqfFYpKlYoTShDTSkqfFYpKihDKE0oQ00pKnxWKSpWKE0oQ00pKnxWKSopKigoUChDUCkqKSsoRChDRCkqKSooQShDQSkqfE4pKk4pK3woKEEoQ0EpKnxOKSpOKChQKENQKSopKyhEKENEKSopKihBKENBKSp8TikqTikqKEMoRChDRCkqKSooQShDQSkqfE4pKk4oKFAoQ1ApKikrKEQoQ0QpKikqKEEoQ0EpKnxOKSpOKSopKikoUChDUCkqKSooKE0oQ00pKnxWKSpWKE0oQ00pKnxWKSooQyhNKENNKSp8VikqVihNKENNKSp8VikqKSooRChDRCkqKSooKEEoQ0EpKnxOKSpOKChQKENQKSopKyhEKENEKSopKihBKENBKSp8TikqTikqKEMoRChDRCkqKSooQShDQSkqfE4pKk4oKFAoQ1ApKikrKEQoQ0QpKikqKEEoQ0EpKnxOKSpOKSopKil8KE0oQ00pKnxWKSpWKE0oQ00pKnxWKSooQyhNKENNKSp8VikqVihNKENNKSp8VikqKSooKFAoQ1ApKikrKEQoQ0QpKikqKEEoQ0EpKnxOKSpOKSspKSIgIyBWZXJiIHBocmFzZSB3aXRoIGNvb3JkaW5hdGlvbiBjb25qdW5jdGlvbgpgYGAKCmBgYHtyfQoKdXAzIDwtIHVwICU+JSBzdWJzZXQoc2VudGVuY2UgJWluJSB1cDIkc2VudGVuY2UpCnVwMyAlPD4lIHNlcGFyYXRlKGRvY19pZCwgYygiZG9jIiwgImdjb2RlIiksIHNlcCA9ICIsICIpCgp1cDMgJTw+JSBtdXRhdGUocGhyYXNlX3RhZz1hc19waHJhc2VtYWNoaW5lKHVwb3MsdHlwZT0idXBvcyIpKQoKdmVyYl9waHJhc2VzIDwtIGtleXdvcmRzX3BocmFzZXModXAzJHBocmFzZV90YWcsIHRlcm0gPSB1cDMkdG9rZW4sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSB2ZXJiX3BocmFzZV9zaW1wLCBpc19yZWdleCA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ncmFtX21heCA9IDcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRldGFpbGVkID0gVFJVRSkKCmhlYWQoc29ydCh0YWJsZSh2ZXJiX3BocmFzZXMka2V5d29yZCksIGRlY3JlYXNpbmc9VFJVRSksIDIwKQpgYGAKCgpgYGB7cn0KbGlicmFyeSh0ZXh0c3RlbSkKdmVyYl9waHJhc2VzMiA8LXZlcmJfcGhyYXNlcwoKbGVtbWVkIDwtIHZlcmJfcGhyYXNlcyAlPiUgdW5uZXN0X3Rva2Vucyh3b3JkLGtleXdvcmQsdG9rZW4gPSAibmdyYW1zIiwgbj0xKSAKbGVtbWVkICU8PiUgbXV0YXRlKGxlbW1hPWxlbW1hdGl6ZV93b3Jkcyh3b3JkLCBkaWN0aW9uYXJ5ID0gbGV4aWNvbjo6aGFzaF9sZW1tYXMpKQpsZW1tZWRfc3MgPC0gbGVtbWVkICU+JSBzdWJzZXQobGVtbWEgJWluJSB2ZXJicyR2ZXJiW3ZlcmJzJGNoYXJhY3Rlcj09IlFQIl0pCnZlcmJfcGhyYXNlczIgJTw+JSBzdWJzZXQoc3RhcnQgJWluJSBsZW1tZWRfc3Mkc3RhcnQgJiBlbmQgJWluJSBsZW1tZWRfc3MkZW5kKQoKCnZlcmJfcGhyYXNlczIgJT4lIAogIGZpbHRlcihuZ3JhbT49MykgJT4lCiAgbWVyZ2UodHh0X2ZyZXEodmVyYl9waHJhc2VzMiRrZXl3b3JkKSwgYnkueD0gImtleXdvcmQiLCBieS55ID0gImtleSIpICU+JSAKICBhcnJhbmdlKGRlc2MoZnJlcSkpICU+JSAKICAjZ3JvdXBfYnkoa2V5d29yZCxmcmVxKSAlPiUgc3VtbWFyaQogIGRpc3RpbmN0KGtleXdvcmQsZnJlcSkKCiMgdmVyYnMkdmVyYlt2ZXJicyRjaGFyYWN0ZXI9PSJRUCJdCgpgYGAKCgpgYGB7cn0KdmVyYl9waHJhc2VzIDwtIGxlbW1lZCAlPiUKICBncm91cF9ieShzdGFydCwgZW5kKSAlPiUKICBzdW1tYXJpc2Uoc3RyaW5nID0gYyh3b3JkKSkgJT4lCiAgcm93d2lzZSgpCgogIG5lc3RfYnkobmdyYW0sIHBhdHRlcm4sIHN0YXJ0LCBlbmQpCmBgYAoKCgoKIyMgVHJ5aW5nIHRvIGZpbmQgY29vY2N1cnJlbmNlcyBhbW91bmcgc2xlY3RlZCB2ZXJicyBhbmQgbm91bnMg4oCUIHNvbWUgdW5jZXJ0YWludHkgaW50cm9kdWNlZCBzaW5jZSBsZW1tYSBjYW4gYmUgYm90aCBub3VuIGFuZCBhIHZlcmIuCmBgYHtyfQpjb29jIDwtIGNvb2NjdXJyZW5jZSh4ID0gc3Vic2V0KHVwMywgdXBvcyAlaW4lIGMoIk5PVU4iLCAiVkVSQiIpKSwgCiAgICAgICAgICAgICAgICAgICAgIHRlcm0gPSAibGVtbWEiLCAKICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBjKCJkb2MiLCAiZ2NvZGUiLCAicGFyYWdyYXBoX2lkIiwgInNlbnRlbmNlX2lkIikpCmNvb2MgJTw+JSBzdWJzZXQodGVybTEgJWluJSB2ZXJicyR2ZXJiIHwgdGVybTIgJWluJSB2ZXJicyR2ZXJiKSAKY29vYyAlPiUgc3RyKCkKYGBgCgoKYGBge3J9CndvcmRuZXR3b3JrIDwtIGhlYWQoY29vYywgNTApCndvcmRuZXR3b3JrIDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSh3b3JkbmV0d29yaykKZ2dyYXBoKHdvcmRuZXR3b3JrLCBsYXlvdXQgPSAiZnIiKSArCiAgZ2VvbV9lZGdlX2xpbmsoYWVzKHdpZHRoID0gY29vYywgZWRnZV9hbHBoYSA9IGNvb2MpLCBlZGdlX2NvbG91ciA9ICJsaWdodGJsdWUiKSArCiAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIGNvbCA9ICJibHVlIiwgc2l6ZSA9IDQpICsKICB0aGVtZV9ncmFwaChiYXNlX2ZhbWlseSA9ICJTYW5zIFNlcmlmIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGxhYnModGl0bGUgPSAiQ29vY2N1cnJlbmNlcyB3aXRoaW4gc2VudGVuY2UiLCBzdWJ0aXRsZSA9ICJOb3VucyAmIFZlcmJzIikKCmBgYAojIyBNZXJnaW5nIGRvbmUgYmFkbHkKYGBge3J9CnRlbXBkczwtdXAyICU+JSBzZWxlY3QoZG9jLCBnY29kZSwgcGFyYWdyYXBoX2lkLCBzZW50ZW5jZV9pZCwgZnJlcSwgZnJlcV9wY3QsIGxlbW1hLCBjaGFyYWN0ZXIpIAoKdGVtcGRzIDwtIGxlZnRfam9pbih1cDMsIHRlbXBkcywgYnk9YygiZG9jIiwgImdjb2RlIiwgInBhcmFncmFwaF9pZCIsICJzZW50ZW5jZV9pZCIpKQoKdGVtcGRzICU8PiUgZGlzdGluY3QoZG9jLCBnY29kZSwgcGFyYWdyYXBoX2lkLCBzZW50ZW5jZV9pZCwgdG9rZW5faWQsIC5rZWVwX2FsbCA9IFQpCmBgYAoKCiMjIFRvcCA1IHZlcmJzIGluIGV2ZXJ5IHZlcmIgZ3JvdXAKYGBge3J9CnQ1diA8LSB1cDIgJT4lIAogIHNlbGVjdChsZW1tYSxjaGFyYWN0ZXIsZnJlcSkgJT4lCiAgZGlzdGluY3QoKSAlPiUgCiAgZ3JvdXBfYnkoY2hhcmFjdGVyKSAlPiUgCiAgYXJyYW5nZShkZXNjKGZyZXEpKSAlPiUgCiAgc2xpY2UoMTo1KSAlPiUgCiAgdW5ncm91cCgpCnQ1dgpgYGAKCiMjIE1vc3QgY29vY2N1cmVkIG5vdW5zIHdpdGggdmVyYnMgYnkgY2hhcmFjdGVyIGluIDQwMSBjb2RlCgpgYGB7cn0KZnJlcSA8LSB0ZW1wZHMgJT4lCiAgZmlsdGVyKGdjb2RlPT0iNDAxIikgJT4lIAogIHN1YnNldChsZW1tYS55ICVpbiUgdDV2JGxlbW1hICYgdXBvcyAlaW4lICJOT1VOIikgJT4lCiAgZ3JvdXBfYnkobGVtbWEueSkgJT4lIGNvdW50KGxlbW1hLngpICU+JSBtdXRhdGUoZnJlcSA9IG4gLyBzdW0obikpICU+JSB1bmdyb3VwKCkKZnJlcQpmcmVxICU+JSAKICBncm91cF9ieShsZW1tYS55KSAlPiUgYXJyYW5nZShkZXNjKGZyZXEpKSAlPiUgc2xpY2UoMTo2KSAlPiUgdW5ncm91cCgpICU+JSAKICBtZXJnZSh0NXYgJT4lIHNlbGVjdChsZW1tYSxjaGFyYWN0ZXIpLCBieS54PSJsZW1tYS55IiwgYnkueT0ibGVtbWEiKSAlPiUgCiAgbXV0YXRlKGxlbW1hLnkgPSBzdHJfYygiKCIsY2hhcmFjdGVyLCIpICIsbGVtbWEueSwgc2VwPSIiKSwKICAgICAgICAgbm91biA9IHJlb3JkZXJfd2l0aGluKGxlbW1hLngsIGJ5ID0gbiwgd2l0aGluID0gbGVtbWEueSkpICU+JQogIGdncGxvdChhZXMoeCA9IG5vdW4sIHkgPSBuLCBmaWxsID0gY2hhcmFjdGVyKSkgKwogICAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogICAgbGFicyh4ID0gIk5vdW5zIiwgeSA9ICJmcmVxdWVuY3kiKSArCiAgICBmYWNldF93cmFwKH5sZW1tYS55LCBuY29sID0gNCwgc2NhbGVzID0gImZyZWVfeSIpICsKICAgIGNvb3JkX2ZsaXAoKSArCiAgICBzY2FsZV94X3Jlb3JkZXJlZCgpICsKICAgICNmYWNldF9ncmlkKH5jaGFyYWN0ZXIpKwogICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpKQoKYGBgCgojIyBUb3AgTm91bnMgY29vY2N1cmVkIHdpdGggdmVyYnMgYnkgZXZlcnkgdmVyYi1jaGFyYWN0ZXIKYGBge3J9CiAKdGVtcGRzICU+JQogIHN1YnNldChsZW1tYS55ICVpbiUgdDV2JGxlbW1hICYgdXBvcyAlaW4lICJOT1VOIikgJT4lCiAgZ3JvdXBfYnkoZ2NvZGUsY2hhcmFjdGVyKSAlPiUgY291bnQobGVtbWEueCkgJT4lIG11dGF0ZShmcmVxID0gbiAvIHN1bShuKSkgJT4lIHVuZ3JvdXAoKSAlPiUgCiAgZmlsdGVyKGdjb2RlPT0iNDAxIikgJT4lIAogIGdyb3VwX2J5KGNoYXJhY3RlcikgJT4lIGFycmFuZ2UoZGVzYyhuKSkgJT4lIHNsaWNlKDE6MTIpICU+JSB1bmdyb3VwKCkgJT4lCiAgI211dGF0ZShub3VuID0gcmVvcmRlcl93aXRoaW4obGVtbWEueCwgYnkgPSBuLCB3aXRoaW4gPSBjaGFyYWN0ZXIpLCkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbGVtbWEueCwgeSA9IG4sIGZpbGwgPSBjaGFyYWN0ZXIpKSArCiAgICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgICBsYWJzKHggPSAiTm91bnMiLCB5ID0gImZyZXF1ZW5jeSIpICsKICAgICNmYWNldF93cmFwKH5jaGFyYWN0ZXIsIG5jb2wgPSA2LCBzY2FsZXMgPSAiZnJlZV95IikgKwogICAgY29vcmRfZmxpcCgpICsKICAgICNzY2FsZV94X3Jlb3JkZXJlZCgpICsKICAgIGZhY2V0X2dyaWQofmNoYXJhY3RlcikrCiAgICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gNikpCgpgYGAKCgoKCgoKYGBge3J9CgpsaWJyYXJ5KGlncmFwaCkKbGlicmFyeShnZ3JhcGgpCmxpYnJhcnkoZ2dwbG90MikKcGxvdF9hbm5vdGF0aW9uIDwtIGZ1bmN0aW9uKHgsIHNpemUgPSAzKXsKICBzdG9waWZub3QoaXMuZGF0YS5mcmFtZSh4KSAmIGFsbChjKCJzZW50ZW5jZV9pZCIsICJ0b2tlbl9pZCIsICJoZWFkX3Rva2VuX2lkIiwgImRlcF9yZWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRva2VuIiwgImxlbW1hIiwgInVwb3MiLCAieHBvcyIsICJmZWF0cyIpICVpbiUgY29sbmFtZXMoeCkpKQogIHggPC0geFshaXMubmEoeCRoZWFkX3Rva2VuX2lkKSwgXQogIHggPC0geFt4JHNlbnRlbmNlX2lkICVpbiUgbWluKHgkc2VudGVuY2VfaWQpLCBdCiAgZWRnZXMgPC0geFt4JGhlYWRfdG9rZW5faWQgIT0gMCwgYygidG9rZW5faWQiLCAiaGVhZF90b2tlbl9pZCIsICJkZXBfcmVsIildCiAgZWRnZXMkbGFiZWwgPC0gZWRnZXMkZGVwX3JlbAogIGcgPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGVkZ2VzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRpY2VzID0geFssIGMoInRva2VuX2lkIiwgInRva2VuIiwgImxlbW1hIiwgInVwb3MiLCAieHBvcyIsICJmZWF0cyIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXJlY3RlZCA9IFRSVUUpCiAgZ2dyYXBoKGcsIGxheW91dCA9ICJsaW5lYXIiKSArCiAgICBnZW9tX2VkZ2VfYXJjKGdncGxvdDI6OmFlcyhsYWJlbCA9IGRlcF9yZWwsIHZqdXN0ID0gLTAuMjApLAogICAgICAgICAgICAgICAgICBhcnJvdyA9IGdyaWQ6OmFycm93KGxlbmd0aCA9IHVuaXQoNCwgJ21tJyksIGVuZHMgPSAibGFzdCIsIHR5cGUgPSAiY2xvc2VkIiksCiAgICAgICAgICAgICAgICAgIGVuZF9jYXAgPSBnZ3JhcGg6OmxhYmVsX3JlY3QoIndvcmRzd29yZHN3b3JkcyIpLAogICAgICAgICAgICAgICAgICBsYWJlbF9jb2xvdXIgPSAicmVkIiwgY2hlY2tfb3ZlcmxhcCA9IFRSVUUsIGxhYmVsX3NpemUgPSBzaXplKSArCiAgICBnZW9tX25vZGVfbGFiZWwoZ2dwbG90Mjo6YWVzKGxhYmVsID0gdG9rZW4pLCBjb2wgPSAiZGFya2dyZWVuIiwgc2l6ZSA9IHNpemUsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgICBnZW9tX25vZGVfdGV4dChnZ3Bsb3QyOjphZXMobGFiZWwgPSB1cG9zKSwgbnVkZ2VfeSA9IC0wLjM1LCBzaXplID0gc2l6ZSkgKwogICAgdGhlbWVfZ3JhcGgoYmFzZV9mYW1pbHkgPSAiQXJpYWwgTmFycm93IikgKwogICAgbGFicyh0aXRsZSA9ICJ1ZHBpcGUgb3V0cHV0Iiwgc3VidGl0bGUgPSAidG9rZW5pc2F0aW9uLCBwYXJ0cyBvZiBzcGVlY2ggdGFnZ2luZyAmIGRlcGVuZGVuY3kgcmVsYXRpb25zIikKfQoKdXAzWzY0NTo2NjMsXQpwbG90X2Fubm90YXRpb24odXAzWzY0NTo2NjMsXSkKCgpgYGAKYGBge3J9Cgp1cDQgPC0gdXAyICU+JSBmaWx0ZXIoZGVwX3JlbD09InJvb3QiKQoKdXA0ICU+JSBncm91cF9ieShjaGFyYWN0ZXIpICU+JSBjb3VudCgpCgpgYGAKCgoKCgoKCmBgYHtyfQp1bmRhdGEyIDwtIHByZV9wcm9fcmVwICU+JSAKICB1bm5lc3RfdG9rZW5zKGJpZ3JhbSx0ZXh0LHRva2VuID0gIm5ncmFtcyIsIG49MikgIyU+JSAKICAjbXV0YXRlKGJpZ3JhbSA9IGJpZ3JhbSAlPiUgc3RyX3JlbW92ZV9hbGwoIlteWzphbG51bTpdXSIpKSAlPiUgCgp1bmRhdGExIDwtIHByZV9wcm9fcmVwICU+JSAKICB1bm5lc3RfdG9rZW5zKHdvcmQsdGV4dCx0b2tlbiA9ICJuZ3JhbXMiLCBuPTEpICU+JSAKICBtdXRhdGUod29yZCA9IHdvcmQgJT4lIHN0cl9yZW1vdmVfYWxsKCJbXls6YWxudW06XV0iKSkgJT4lIAogIG11dGF0ZSh3b3JkID0gd29yZCAlPiUgc3RyX3JlbW92ZV9hbGwocmVtX2RpZykpICU+JSAKICBtdXRhdGUod29yZCA9IHdvcmQgJT4lIHN0cl9yZW1vdmVfYWxsKCJudWxsIikpICU+JSAKICBmaWx0ZXIoIWlzLm5hKHdvcmQpKSAlPiUgCiAgYW50aV9qb2luKHN0b3Bfd29yZHMsIGJ5ID0gIndvcmQiKQoKYmlncmFtc19zZXBhcmF0ZWQgPC0gdW5kYXRhMiAlPiUgc2VwYXJhdGUoYmlncmFtLCBjKCJ3b3JkMSIsICJ3b3JkMiIpLCBzZXAgPSAiICIpCmJpZ3JhbXNfZmlsdGVyZWQgPC0gYmlncmFtc19zZXBhcmF0ZWQgJT4lIGZpbHRlcighd29yZDEgJWluJSBzdG9wX3dvcmRzJHdvcmQpICU+JSBmaWx0ZXIoIXdvcmQyICVpbiUgc3RvcF93b3JkcyR3b3JkKQoKIyBuZXcgYmlncmFtIGNvdW50czoKYmlncmFtX2NvdW50cyA8LSBiaWdyYW1zX2ZpbHRlcmVkICU+JSBjb3VudCh3b3JkMSwgd29yZDIsIHNvcnQgPSBUUlVFKQpiaWdyYW1zX3VuaXRlZCA8LSBiaWdyYW1zX2ZpbHRlcmVkICU+JSB1bml0ZShiaWdyYW0sIHdvcmQxLCB3b3JkMiwgc2VwID0gIiAiKQpiaWdyYW1zX3VuaXRlZCAlPD4lIGFkZF9jb3VudChYLGJpZ3JhbSkgJT4lIGJpbmRfdGZfaWRmKHRlcm0gPSBiaWdyYW0sIGRvY3VtZW50ID0gWCwgbiA9IG4pCgp3b3JkcyA8LSB1bmRhdGExICU+JSBhZGRfY291bnQoWCx3b3JkKSAlPiUgYmluZF90Zl9pZGYodGVybSA9IHdvcmQsIGRvY3VtZW50ID0gWCwgbiA9IG4pCgpiaWdyYW1zX3VuaXRlZCAlPiUgY291bnQoYmlncmFtLCB3dCA9IHRmX2lkZiwgc29ydCA9IFRSVUUpICU+JSBoZWFkKDI1KQoKdG9wX2J5X2cgPC0gYmlncmFtc191bml0ZWQgJT4lIGdyb3VwX2J5KGdjb2RlKSAlPiUgY291bnQoYmlncmFtLCB3dCA9IHRmX2lkZiwgc29ydCA9IFRSVUUsIG5hbWUgPSAidGZfaWRmIikgJT4lIGRwbHlyOjpzbGljZSgxOjEyKSAlPiUgdW5ncm91cCgpCgp0b3BfYnlfZyA8LSB3b3JkcyAlPiUgZ3JvdXBfYnkoZ2NvZGUpICU+JSBjb3VudCh3b3JkLCB3dCA9IHRmX2lkZiwgc29ydCA9IFRSVUUsIG5hbWUgPSAidGZfaWRmIikgJT4lIGRwbHlyOjpzbGljZSgxOjUpICU+JSB1bmdyb3VwKCkKCnRvcF9ieV95IDwtIGJpZ3JhbXNfdW5pdGVkICU+JSBncm91cF9ieShyZXBfeWVhcikgJT4lIGNvdW50KGJpZ3JhbSwgd3QgPSB0Zl9pZGYsIHNvcnQgPSBUUlVFLCBuYW1lID0gInRmX2lkZiIpICU+JSBkcGx5cjo6c2xpY2UoMToxMikgJT4lIHVuZ3JvdXAoKQoKIyB0b3AgYmlncmFtcyBpbiBlYWNoIGdfY29kZQp0b3BfYnlfZyAlPiUgbXV0YXRlKGJpZ3JhbSA9IHJlb3JkZXJfd2l0aGluKGJpZ3JhbSwgYnkgPSB0Zl9pZGYsIHdpdGhpbiA9IGdjb2RlKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gYmlncmFtLCB5ID0gdGZfaWRmLCBmaWxsID0gZ2NvZGUpKSArCiAgICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgICBsYWJzKHggPSBOVUxMLCB5ID0gInRmLWlkZiIpICsKICAgIGZhY2V0X3dyYXAofmdjb2RlLCBuY29sID0gMywgc2NhbGVzID0gImZyZWUiKSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgc2NhbGVfeF9yZW9yZGVyZWQoKSArCiAgICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gNikpCgoKdG9wX2J5X2cgJT4lIG11dGF0ZSh3b3JkID0gcmVvcmRlcl93aXRoaW4od29yZCwgYnkgPSB0Zl9pZGYsIHdpdGhpbiA9IGdjb2RlKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gd29yZCwgeSA9IHRmX2lkZiwgZmlsbCA9IGdjb2RlKSkgKwogICAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogICAgbGFicyh4ID0gTlVMTCwgeSA9ICJ0Zi1pZGYiKSArCiAgICBmYWNldF93cmFwKH5nY29kZSwgbmNvbCA9IDMsIHNjYWxlcyA9ICJmcmVlIikgKwogICAgY29vcmRfZmxpcCgpICsKICAgIHNjYWxlX3hfcmVvcmRlcmVkKCkgKwogICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKQoKYGBgCgpgYGB7cn0KbGlicmFyeSh0ZXh0ZGF0YSkKbGlicmFyeShyZWNpcGVzKQpsaWJyYXJ5KHRpZHltb2RlbHMpCmxpYnJhcnkodGV4dHJlY2lwZXMpCmxpYnJhcnkodGhlbWlzKQpsaWJyYXJ5KHR1bmUpCmxpYnJhcnkoZ2xtbmV0KQpsaWJyYXJ5KHJhbmdlcikKYGBgCgoKYGBge3J9Cm1kYXRhIDwtIG9jcHQyICU+JSB1bmdyb3VwKCkgJT4lICBzZWxlY3QoZ2NvZGUsdGV4dCkKCiNnbG92ZTZiIDwtIGVtYmVkZGluZ19nbG92ZTZiKGRpbWVuc2lvbnMgPSAxMDApCgpzZXQuc2VlZCgxMjM0KQoKcmVtX3B1bmN0IDwtIHJlZ2V4KCJbWzpwdW5jdDpdXSIpCnJlbV9kaWcgPC0gcmVnZXgoIltbOmRpZ2l0Ol1dIikKCm1kYXRhICU8PiUgbXV0YXRlKHRleHQ9c3RyX3NxdWlzaCh0ZXh0KSwKICAgICAgICAgICAgICAgICAgdGV4dD1zdHJfcmVtb3ZlX2FsbCh0ZXh0LHJlbV9wdW5jdCksCiAgICAgICAgICAgICAgICAgIHRleHQ9c3RyX3JlbW92ZV9hbGwodGV4dCxyZW1fZGlnKSkKYGBgCgoKYGBge3J9Cm1kYXRhICU+JSBncm91cF9ieShnY29kZSkgJT4lIGNvdW50KHNvcnQgPSBUKSMgJT4lIHNsaWNlKDE6OCkgJT4lIHVuZ3JvdXAoKSAjICU+JSBnZ3Bsb3QoYWVzKHggPSBnY29kZSwgeSA9IG4pKSArIGdlb21fY29sKCkKCm1kYXRhICU8PiUgZmlsdGVyKGdjb2RlICVpbiUgYygiNDAxIiwiNDAzIiwiNDA0IiwgIjQwNyIsICI0MDUiLCI0MTMiLCI0MTQiLCI0MTciLCI0MTYiLCI5OTkiKSkKIyBtZGF0YSAlPD4lIGZpbHRlcighZ2NvZGUgJWluJSBjKCI0MDkiLCI0MTAiLCI0MTEiKSkKIyBtZGF0YSAlPD4lIGRyb3BfbmEoKQptZGF0YSAlPD4lIG11dGF0ZShnY29kZT1hcy5mYWN0b3IoZ2NvZGUpKQoKI2Rvd25zYW1wbGluZwoKbWRhdGEgJT4lIGZpbHRlcihnY29kZSAlaW4lIGMoIjk5OSIpKQoKbm9fbGFiZWxfaW5kZXggPC0gd2hpY2gobWRhdGEkZ2NvZGU9PTk5OSkKIyBsYWJlbGVkX2luZGV4IDwtIHdoaWNoKCFtZGF0YSRnY29kZT09OTk5KQpyYW5kb21faW5kZXhlcyA8LSBzYW1wbGUoMTpsZW5ndGgobm9fbGFiZWxfaW5kZXgpLCBsZW5ndGgobm9fbGFiZWxfaW5kZXgpLTIwMCwgcmVwbGFjZT1GKQojIHJhbmRvbV9kb3duc2FtcGxlIDwtIG5vX2xhYmVsX2luZGV4Wy1jKHJhbmRvbV9pbmRleGVzKV0KCm1kYXRhIDwtbWRhdGFbLWMobm9fbGFiZWxfaW5kZXhbYyhyYW5kb21faW5kZXhlcyldKSxdCmBgYAoKCmBgYHtyfQp3cml0ZS5jc3YobWRhdGEsICJtZGF0YV8yLmNzdiIpCmBgYAoKCmBgYHtyfQp0aWR5X3NwbGl0IDwtIGluaXRpYWxfc3BsaXQobWRhdGEsIHN0cmF0YSA9IGdjb2RlLCBwcm9wID0gMC43KQp0cmFpbl9kYXRhIDwtIHRyYWluaW5nKHRpZHlfc3BsaXQpCnRlc3RfZGF0YSA8LSB0ZXN0aW5nKHRpZHlfc3BsaXQpCnRpZHlfc3BsaXQKYGBgCgoKYGBge3J9CnRyYWluX2RhdGEgPC0gcmVjaXBlKGdjb2Rlfi4sIGRhdGEgPSB0cmFpbl9kYXRhKSAlPiUgdGhlbWlzOjpzdGVwX3Vwc2FtcGxlKGdjb2RlKSAlPiUgcHJlcCgpICU+JSBqdWljZSgpCgoKdHJhaW5fZGF0YSAlPD4lIG11dGF0ZSh0ZXh0PWFzLmNoYXJhY3Rlcih0ZXh0KSkKYGBgCgoKYGBge3J9CmRhdGFfcmVzIDwtIHRyYWluX2RhdGEgJT4lIHZmb2xkX2N2KHN0cmF0YSA9IGdjb2RlLCB2ID0gMTAsIHJlcGVhdHMgPSAzKQpkYXRhX3JlcyA8LSB2Zm9sZF9jdih0cmFpbl9kYXRhKQpgYGAKCgpgYGB7cn0KdGZfaWRmX3JlYyA8LSByZWNpcGUoZ2NvZGUgfiAuLCBkYXRhID0gdHJhaW5fZGF0YSkgJT4lCiAgc3RlcF90b2tlbml6ZSh0ZXh0KSAlPiUKICBzdGVwX3N0ZW0odGV4dCkgJT4lCiAgc3RlcF9zdG9wd29yZHModGV4dCkgJT4lCiAgc3RlcF90b2tlbmZpbHRlcih0ZXh0LCBtYXhfdG9rZW5zID0gMTAwMCkgJT4lCiAgc3RlcF90ZmlkZihhbGxfcHJlZGljdG9ycygpKQoKdGZfaWRmX2RhdGEgPC0gdGZfaWRmX3JlYyAlPiUgcHJlcCgpICU+JSBqdWljZSgpCmBgYAoKCmBgYHtyfQpoYXNoX3JlYyA8LSByZWNpcGUoZ2NvZGV+LiwgZGF0YSA9IHRyYWluX2RhdGEpICU+JQogIHN0ZXBfdG9rZW5pemUodGV4dCkgJT4lCiAgc3RlcF9zdGVtKHRleHQpICU+JQogIHN0ZXBfc3RvcHdvcmRzKHRleHQpICU+JQogIHN0ZXBfdG9rZW5maWx0ZXIodGV4dCwgbWF4X3Rva2VucyA9IDEwMDApICU+JQogIHN0ZXBfdGV4dGhhc2godGV4dCwgbnVtX3Rlcm1zID0gMTAwKQoKaGFzaF9yZWMgJT4lIHByZXAoKSAlPiUganVpY2UoKQoKYGBgCgoKYGBge3J9Cm1vZGVsX2xnIDwtIG11bHRpbm9tX3JlZygpICU+JQogIHNldF9hcmdzKHBlbmFsdHk9dHVuZSgpLCBtaXh0dXJlPU5VTEwpICU+JSAKICBzZXRfZW5naW5lKCJnbG1uZXQiKSAlPiUKICBzZXRfbW9kZSgiY2xhc3NpZmljYXRpb24iKQoKbW9kZWxfcmYgPC0gcmFuZF9mb3Jlc3QoKSAlPiUKICBzZXRfZW5naW5lKCJyYW5nZXIiLCBpbXBvcnRhbmNlID0gImltcHVyaXR5IikgJT4lCiAgc2V0X21vZGUoImNsYXNzaWZpY2F0aW9uIikKCmBgYAoKCgpgYGB7cn0KbG9naXN0aWNfZ3JpZCA8LSBncmlkX3JlZ3VsYXIocGFyYW1ldGVycyhtb2RlbF9sZyksIGxldmVscyA9IDMpCm1vZGVsX2NvbnRyb2wgPC0gY29udHJvbF9ncmlkKHNhdmVfcHJlZCA9IFRSVUUpCm1vZGVsX21ldHJpY3MgPC0gbWV0cmljX3NldChhY2N1cmFjeSwgcm9jX2F1YykKYGBgCgoKYGBge3J9CmxpbmVhcl90Zl9yZXMgPC0gdHVuZV9ncmlkKG1vZGVsX2xnLCB0Zl9pZGZfcmVjLCBncmlkID0gbG9naXN0aWNfZ3JpZCwgY29udHJvbCA9IG1vZGVsX2NvbnRyb2wsIG1ldHJpY3MgPSBtb2RlbF9tZXRyaWNzLCByZXNhbXBsZXMgPSBkYXRhX3JlcykKbGluZWFyX2hhc2hfcmVzIDwtIHR1bmVfZ3JpZChtb2RlbF9sZywgaGFzaF9yZWMsIGdyaWQgPSBsb2dpc3RpY19ncmlkLCBjb250cm9sID0gbW9kZWxfY29udHJvbCwgbWV0cmljcyA9IG1vZGVsX21ldHJpY3MsIHJlc2FtcGxlcyA9IGRhdGFfcmVzKQoKYGBgCgoKCmBgYHtyfQp3b3JrZmxvd19nZW5lcmFsX3RmIDwtIHdvcmtmbG93KCkgJT4lIGFkZF9yZWNpcGUodGZfaWRmX3JlYykKd29ya2Zsb3dfbGdfdGYgPC0gd29ya2Zsb3dfZ2VuZXJhbF90ZiAlPiUgYWRkX21vZGVsKG1vZGVsX2xnKQp3b3JrZmxvd19yZl90ZiA8LSB3b3JrZmxvd19nZW5lcmFsX3RmICU+JSBhZGRfbW9kZWwobW9kZWxfcmYpCgp3b3JrZmxvd19nZW5lcmFsX2hhc2ggPC0gd29ya2Zsb3coKSAlPiUgYWRkX3JlY2lwZShoYXNoX3JlYykKd29ya2Zsb3dfbGdfaGFzaCA8LSB3b3JrZmxvd19nZW5lcmFsX2hhc2ggJT4lIGFkZF9tb2RlbChtb2RlbF9sZykKd29ya2Zsb3dfcmZfaGFzaCA8LSB3b3JrZmxvd19nZW5lcmFsX2hhc2ggJT4lIGFkZF9tb2RlbChtb2RlbF9yZikKYGBgCgoKYGBge3J9CmxpbmVhcl90Zl9yZXMgJT4lIGF1dG9wbG90KCkKYmVzdF9wYXJhbV9saW5lYXJfdGZfcmVzIDwtIGxpbmVhcl90Zl9yZXMgJT4lIHNlbGVjdF9iZXN0KG1ldHJpYyA9ICdhY2N1cmFjeScpCmJlc3RfcGFyYW1fbGluZWFyX3RmX3Jlcwp3b3JrZmxvd19maW5hbF9sZ190ZiA8LSB3b3JrZmxvd19sZ190ZiAlPiUKICBmaW5hbGl6ZV93b3JrZmxvdyhwYXJhbWV0ZXJzID0gYmVzdF9wYXJhbV9saW5lYXJfdGZfcmVzKQoKbG9nX3Jlc190ZiA8LSB3b3JrZmxvd19maW5hbF9sZ190ZiAlPiUKICBmaXRfcmVzYW1wbGVzKHJlc2FtcGxlcyA9IGRhdGFfcmVzLAogICAgICAgICAgICAgICAgbWV0cmljcyA9IG1ldHJpY19zZXQocmVjYWxsLCBwcmVjaXNpb24sIGZfbWVhcywgYWNjdXJhY3ksIGthcCwgcm9jX2F1Yywgc2VucyksIGNvbnRyb2wgPSBjb250cm9sX3Jlc2FtcGxlcyhzYXZlX3ByZWQgPSBUUlVFKSkKCmxvZ19yZXNfdGYgJT4lIGNvbGxlY3RfbWV0cmljcyhzdW1tYXJpemUgPSBUUlVFKQpgYGAKCgpgYGB7cn0KbGluZWFyX2hhc2hfcmVzICU+JSBhdXRvcGxvdCgpCmJlc3RfcGFyYW1fbGluZWFyX2hhc2hfcmVzIDwtIGxpbmVhcl9oYXNoX3JlcyAlPiUgc2VsZWN0X2Jlc3QobWV0cmljID0gJ2FjY3VyYWN5JykKYmVzdF9wYXJhbV9saW5lYXJfaGFzaF9yZXMKCndvcmtmbG93X2ZpbmFsX2xnX2hhc2ggPC0gd29ya2Zsb3dfbGdfaGFzaCAlPiUKICBmaW5hbGl6ZV93b3JrZmxvdyhwYXJhbWV0ZXJzID0gYmVzdF9wYXJhbV9saW5lYXJfaGFzaF9yZXMpCgpsb2dfcmVzX2hhc2ggPC0gd29ya2Zsb3dfZmluYWxfbGdfaGFzaCAlPiUKICBmaXRfcmVzYW1wbGVzKCByZXNhbXBsZXMgPSBkYXRhX3JlcywKICAgICAgICAgICAgICAgICBtZXRyaWNzID0gbWV0cmljX3NldChyZWNhbGwsIHByZWNpc2lvbiwgZl9tZWFzLCBhY2N1cmFjeSwga2FwLCByb2NfYXVjLCBzZW5zKSwgY29udHJvbCA9IGNvbnRyb2xfcmVzYW1wbGVzKHNhdmVfcHJlZCA9IFRSVUUpKQoKbG9nX3Jlc19oYXNoICU+JSBjb2xsZWN0X21ldHJpY3Moc3VtbWFyaXplID0gVFJVRSkKYGBgCgpgYGB7cn0KCnJmX3Jlc19oYXNoIDwtIHdvcmtmbG93X3JmX2hhc2ggJT4lCiAgZml0X3Jlc2FtcGxlcyhyZXNhbXBsZXMgPSBkYXRhX3JlcywKICAgICAgICAgICAgICAgIG1ldHJpY3MgPSBtZXRyaWNfc2V0KHJlY2FsbCwgcHJlY2lzaW9uLCBmX21lYXMsIGFjY3VyYWN5LCBrYXAsIHJvY19hdWMsIHNlbnMpLAogICAgICAgICAgICAgICAgY29udHJvbCA9IGNvbnRyb2xfcmVzYW1wbGVzKCBzYXZlX3ByZWQgPSBUUlVFKSkKCnJmX3Jlc19oYXNoICU+JSBjb2xsZWN0X21ldHJpY3Moc3VtbWFyaXplID0gVFJVRSkKCmBgYAoKCmBgYHtyfQpyZl9yZXNfdGYgPC0gd29ya2Zsb3dfcmZfdGYgJT4lCiAgZml0X3Jlc2FtcGxlcyhyZXNhbXBsZXMgPSBkYXRhX3JlcywKICAgICAgICAgICAgICAgIG1ldHJpY3MgPSBtZXRyaWNfc2V0KHJlY2FsbCwgcHJlY2lzaW9uLCBmX21lYXMsIGFjY3VyYWN5LCBrYXAsIHJvY19hdWMsIHNlbnMpLAogICAgICAgICAgICAgICAgY29udHJvbCA9IGNvbnRyb2xfcmVzYW1wbGVzKCBzYXZlX3ByZWQgPSBUUlVFKSkKcmZfcmVzX3RmICU+JSBjb2xsZWN0X21ldHJpY3Moc3VtbWFyaXplID0gVFJVRSkKCmBgYApgYGB7cn0KbG9nX21ldHJpY3NfdGYgPC0gbG9nX3Jlc190ZiAlPiUgY29sbGVjdF9tZXRyaWNzKHN1bW1hcmlzZSA9IFRSVUUpICU+JSBtdXRhdGUobW9kZWwgPSAiTG9naXN0aWMgUmVncmVzc2lvbiBURi1pZGYiKQpsb2dfbWV0cmljc19oYXNoIDwtIGxvZ19yZXNfaGFzaCAlPiUgY29sbGVjdF9tZXRyaWNzKHN1bW1hcmlzZSA9IFRSVUUpICU+JSBtdXRhdGUobW9kZWwgPSAiTG9naXN0aWMgUmVncmVzc2lvbiBIYXNoIikKcmZfbWV0cmljc190ZiA8LSByZl9yZXNfdGYgJT4lIGNvbGxlY3RfbWV0cmljcyhzdW1tYXJpc2UgPSBUUlVFKSAlPiUgbXV0YXRlKG1vZGVsID0gIlJhbmRvbSBGb3Jlc3QgVEYtaWRmIikKcmZfbWV0cmljc19oYXNoIDwtIHJmX3Jlc19oYXNoICU+JSBjb2xsZWN0X21ldHJpY3Moc3VtbWFyaXNlID0gVFJVRSkgJT4lIG11dGF0ZShtb2RlbCA9ICJSYW5kb20gRm9yZXN0IEhhc2giKQoKCm1vZGVsX2NvbXBhcmUgPC0gYmluZF9yb3dzKAogIGxvZ19tZXRyaWNzX3RmLCAKICBsb2dfbWV0cmljc19oYXNoLCAKICByZl9tZXRyaWNzX3RmLCAKICByZl9tZXRyaWNzX2hhc2gpCgoKCnJmX3N0YXQxIDwtIHJmX3Jlc190ZiAlPiUgY29sbGVjdF9tZXRyaWNzKHN1bW1hcml6ZSA9IFRSVUUpCnJmX3N0YXQxCmBgYAoKCmBgYHtyfQptb2RlbF9jb21wIDwtIG1vZGVsX2NvbXBhcmUgJT4lCiAgc2VsZWN0KG1vZGVsLCAubWV0cmljLCBtZWFuLCBzdGRfZXJyKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gLm1ldHJpYywgdmFsdWVzX2Zyb20gPSBjKG1lYW4sIHN0ZF9lcnIpKQoKCm1vZGVsX2NvbXAgJT4lCiAgYXJyYW5nZShtZWFuX2ZfbWVhcykgJT4lCiAgICBtdXRhdGUobW9kZWwgPSBmY3RfcmVvcmRlcihtb2RlbCwgbWVhbl9mX21lYXMpKSAlPiUKICAgICAgZ2dwbG90KGFlcyhtb2RlbCwgbWVhbl9mX21lYXMsIGZpbGw9bW9kZWwpKSArCiAgICAgIGdlb21fY29sKCkgKyBjb29yZF9mbGlwKCkgKwogICAgICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIllsR24iKSArCiAgICAgIGdlb21fdGV4dChzaXplID0gMywgYWVzKGxhYmVsID0gcm91bmQobWVhbl9mX21lYXMsIDIpLCB5ID0gbWVhbl9mX21lYXMgKyAwLjA4KSwgdmp1c3QgPSAxKQoKYGBgCgpgYGB7cn0KcmZfcHJlZF90ZiA8LSByZl9yZXNfdGYgJT4lCiAgY29sbGVjdF9wcmVkaWN0aW9ucygpCgpyZl9wcmVkX3RmICU+JSBjb25mX21hdChnY29kZSwgLnByZWRfY2xhc3MpCgpyZl9wcmVkX3RmICU+JSBjb25mX21hdChnY29kZSwgLnByZWRfY2xhc3MpICU+JSBhdXRvcGxvdCh0eXBlID0gImhlYXRtYXAiKQpgYGAKCmBgYHtyfQoKbGFzdF9maXRfcmYgPC0gbGFzdF9maXQod29ya2Zsb3dfcmZfdGYsCiAgICAgICAgICAgICAgICAgICAgICAgIHNwbGl0ID0gdGlkeV9zcGxpdCwKICAgICAgICAgICAgICAgICAgICAgICAgbWV0cmljcyA9IG1ldHJpY19zZXQocmVjYWxsLCBwcmVjaXNpb24sIGZfbWVhcywgYWNjdXJhY3ksIGthcCwgcm9jX2F1Yywgc2VucykpCgpyZl9zdGF0MiA8LSBsYXN0X2ZpdF9yZiAlPiUgY29sbGVjdF9tZXRyaWNzKCkKcmZfc3RhdDIKCmxhc3RfZml0X3JmICU+JSBjb2xsZWN0X3ByZWRpY3Rpb25zKCkgJT4lIGNvbmZfbWF0KGdjb2RlLCAucHJlZF9jbGFzcykgJT4lIGF1dG9wbG90KHR5cGUgPSAiaGVhdG1hcCIpCgpteXQgPC0gcmZfc3RhdDEgJT4lIGxlZnRfam9pbihyZl9zdGF0MiwgYnk9Ii5tZXRyaWMiKSAlPiUgc2VsZWN0KC5tZXRyaWMsIG1lYW4sIHN0ZF9lcnIsIC5lc3RpbWF0ZSkKbXl0CgpgYGAKCmBgYHtyfQpsaWJyYXJ5KHN0YXJnYXplcikKbGlicmFyeSh4dGFibGUpCiNzdGFyZ2F6ZXIobXl0LCB0eXBlID0gImxhdGV4Iiwgb3V0ID0gIm1vZGVscy50ZXgiKQpzdGFyZ2F6ZXIobXl0LCB0eXBlID0gInRleHQiKQpwcmludCh4dGFibGUobXl0LCB0eXBlID0gImxhdGV4IiksIGZpbGUgPSAiZmlsZW5hbWUyLnRleCIpCgpgYGAKCgo=